#include <espeak-ng/espeak_ng.h>
#define PLAYBACK_MODE (ENOUTPUT_MODE_SYNCHRONOUS | ENOUTPUT_MODE_SPEAK_AUDIO)

#include <vector>
#include <string>

struct segment
{
    char type;
    int start;
    int length;
};

struct pho_event
{
    std::string code;
    int type;
    int start;
};

std::vector<short int> samples;
std::vector<segment> segments;
std::vector<pho_event> pho;
char segment_type = 0;
int segment_pos = 0;

void init()
{
	espeak_ng_InitializePath(NULL);
	
	espeak_ng_ERROR_CONTEXT context = NULL;
	espeak_ng_Initialize(&context);
	
	espeak_ng_InitializeOutput(ENOUTPUT_MODE_SYNCHRONOUS, 0, NULL); //FIX https://notabug.org/isengaara/sekai/issues/4
}

void segment_event(char type)
{
    if(segment_type!=type)
    {
        if(segment_type!=0)
        {
            segment s;
            s.type = segment_type;
            s.start = segment_pos;
            s.length = samples.size()-segment_pos;
            segments.push_back(s);
        }
        segment_type = type;
        segment_pos = samples.size();
    }
}

void outputPhoSymbol(char* pho_code,int pho_type)
{
    pho_event p;
    p.code = pho_code;
    p.type = pho_type;
    p.start = samples.size();
    pho.push_back(p);
	
}

void outputSilence(short int sample)
{
    segment_event('S');
    samples.push_back(sample);
}

void outputUnvoiced(short int sample)
{
   segment_event('U');
   samples.push_back(sample);   
}

void outputVoiced(short int sample)
{
    segment_event('V');
    samples.push_back(sample);
}

void flush()
{
    outputPhoSymbol((char*)"#",0);
    segment_event(0);
}

void write_zstring(FILE* f,std::string s)
{
    fwrite(s.c_str(),1,s.length()+1,f);
}

void write_int(FILE* f,int n)
{
    fwrite(&n,1,sizeof(int),f);
}

void write_short(FILE* f,short n)
{
    fwrite(&n,1,sizeof(short),f);
}

void write_char(FILE* f,char n)
{
    fwrite(&n,1,sizeof(char),f);
}

void write_float(FILE* f,float n)
{
    fwrite(&n,1,sizeof(float),f);
}

int main(int argc,char** argv)
{
    if(argc<6) 
    {
        printf("usage: espeak-sg voice f0 lyric rate filename\n");
        return 0;
    }
    
    char* voice    =      argv[1];
    int f0         = atoi(argv[2]);
    char* lyric    =      argv[3];
    int rate       = atoi(argv[4]);
    char* filename =      argv[5];
    
    
     
    
   
    
	init();
	espeak_ng_SetVoiceByName(voice);
	
	espeak_ng_OUTPUT_HOOKS hooks;
	hooks.outputPhoSymbol = outputPhoSymbol;
	hooks.outputSilence = outputSilence;
	hooks.outputUnvoiced = outputUnvoiced;
	hooks.outputVoiced = outputVoiced;
	espeak_ng_SetOutputHooks(&hooks);
	
	espeak_SetParameter(espeakRATE, rate, 0);
    
    
    float samplerate = (float)espeak_ng_GetSampleRate();
    espeak_ng_SetConstF0(f0);
	espeak_ng_Synthesize(lyric, 0, 0, POS_CHARACTER, 0, 0, NULL, NULL);
    
    flush();
    
    FILE* f = fopen(filename,"w");
    write_zstring(f,"espeak-sg");
    write_int(f,0); //version of the file format
    write_int(f,samplerate);
    write_int(f,f0);
    
    
    write_int(f,pho.size());
    for (auto i : pho)
    {
        write_zstring(f,i.code);
        write_int(f,i.type);
        write_int(f,i.start);
    }
    
    write_int(f,segments.size());
    for (auto i : segments)
    {
        write_char(f, i.type);
        write_int(f, i.start);
        write_int(f,  i.length);
    }
    
    write_int(f,samples.size());
    fwrite(samples.data(),samples.size(),sizeof(short),f);
    
    fclose(f); 
    
    return 0;   
}
